package com.bjkcst.baselib.opcua;

import com.google.common.collect.ImmutableList;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;

/**
 * @author: zgx
 * @email: zhougaoxing@bjkcst.com
 * @date: 2022/12/07
 * @describe:
 */

public class OpcManager {

    /**
     * opcUa客户端链接
     *
     * @param url              opcUa url
     * @param identityProvider 连接方式
     * @return OpcUaClient
     * @throws Exception Exception
     */
    public static OpcUaClient createClient(String url, IdentityProvider identityProvider) throws Exception {
        //opc ua服务端地址
        final String endPointUrl = url;
        Function<List<EndpointDescription>, Optional<EndpointDescription>> listOptionalFunction = endpoints ->
                endpoints.stream()
                        .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                        .findFirst();
        OpcUaClient opcUaClient = OpcUaClient.create(endPointUrl,
                listOptionalFunction,
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english(""))
                                .setApplicationUri("")
                                //访问方式
                                .setIdentityProvider(identityProvider)
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
        return opcUaClient;
    }

    /**
     * 读取节点数据
     *
     * @param client     OPC UA客户端
     * @param identifier identifier
     * @return 节点数据
     * @throws ExecutionException InterruptedException
     */
    public static String readStrNode(OpcUaClient client, String identifier) throws ExecutionException, InterruptedException {
        int namespaceIndex = 6;
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);
        //读取节点数据
        DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
        //标识符
        String identifier2 = String.valueOf(nodeId.getIdentifier());
        System.out.println(identifier2 + ": " + String.valueOf(value.getValue().getValue()));
        return String.valueOf(value.getValue().getValue());
    }

    /**
     * 读取节点列表数据
     *
     * @param client     OPC UA客户端
     * @param identifier identifier
     * @return 节点数据
     * @throws ExecutionException   异常
     * @throws InterruptedException 异常
     */
    public static Boolean[] readListNode(OpcUaClient client, String identifier) throws ExecutionException, InterruptedException {
        int namespaceIndex = 6;
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);
        //读取节点数据
        DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
        //标识符
        String identifier2 = String.valueOf(nodeId.getIdentifier());
        System.out.println(identifier2 + ": " + String.valueOf(value.getValue().getValue()));
        return ((Boolean[]) value.getValue().getValue());
    }

    /**
     * 写入节点数据
     *
     * @param client OPC UA客户端
     * @throws Exception 异常
     */
    public static boolean writeNodeValue(OpcUaClient client, int namespaceIndex, String identifier, Variant variant) throws Exception {
        //节点
        NodeId nodeId = new NodeId(namespaceIndex, identifier);
        //创建数据对象,此处的数据对象一定要定义类型，不然会出现类型错误，导致无法写入
        List<NodeId> list = ImmutableList.of(nodeId);
        DataValue nowValue = new DataValue(variant, null, null);
        //写入节点数据
        StatusCode statusCode = client.writeValue(nodeId, nowValue).join();
        System.out.println("结果：" + statusCode.isGood());
        return statusCode.isGood();
    }
}
